import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.TextAlignment;
import javafx.stage.Stage;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class JumpingGame extends Application {

    // 游戏常量
    private static final int WIDTH = 800;
    private static final int HEIGHT = 600;
    private static final int GROUND_HEIGHT = 500;
    private static final int PLAYER_WIDTH = 30;
    private static final int PLAYER_HEIGHT = 60;
    private static final int OBSTACLE_WIDTH = 20;
    private static final int OBSTACLE_HEIGHT = 40;
    private static final int OBSTACLE_GAP = 300;

    // 游戏状态
    private enum GameState {
        WELCOME, PLAYING, PAUSED, GAME_OVER
    }
    private GameState gameState = GameState.WELCOME;

    // 游戏数据
    private double playerX = 100;
    private double playerY = GROUND_HEIGHT - PLAYER_HEIGHT;
    private double playerVelocityY = 0;
    private boolean isJumping = false;
    private boolean isCharging = false;
    private double jumpCharge = 0;
    private static final double MAX_JUMP_CHARGE = 20;
    private static final double GRAVITY = 0.8;
    private double baseSpeed = 5;
    private double currentSpeed = baseSpeed;
    private int speedIncreaseInterval = 5;
    private double speedIncreaseFactor = 0.5;

    private List<Obstacle> obstacles = new ArrayList<>();
    private Random random = new Random();

    private int score = 0;
    private int highScore = 0;
    private int scoreMultiplier = 1;
    private boolean isDoubleJumpAvailable = false;
    private boolean hasDoubleJumped = false;
    private List<Particle> particles = new ArrayList<>();

    // 音效设置
    private boolean soundEnabled = true;

    // UI元素
    private StackPane root;
    private Canvas gameCanvas;
    private GraphicsContext gc;
    private VBox welcomeMenu;
    private VBox pauseMenu;
    private Label instructionsLabel;
    private VBox gameOverMenu;

    @Override
    public void start(Stage primaryStage) {
        root = new StackPane();
        gameCanvas = new Canvas(WIDTH, HEIGHT);
        gc = gameCanvas.getGraphicsContext2D();

        // 创建界面元素
        createWelcomeMenu();
        createPauseMenu();
        createInstructionsLabel();
        createGameOverMenu();

        // 确保只添加一次
        root.getChildren().clear(); // 先清空
        root.getChildren().addAll(
                gameCanvas,
                welcomeMenu,
                pauseMenu,
                gameOverMenu,
                instructionsLabel
        );

        // 初始状态
        welcomeMenu.setVisible(true);
        pauseMenu.setVisible(false);
        gameOverMenu.setVisible(false);
        instructionsLabel.setVisible(false);

        Scene scene = new Scene(root, WIDTH, HEIGHT);
        scene.setOnKeyPressed(this::handleKeyPressed);
        scene.setOnKeyReleased(this::handleKeyReleased);

        AnimationTimer timer = new AnimationTimer() {
            @Override
            public void handle(long now) {
                update();
                render();
            }
        };

        primaryStage.setTitle("增强版跳跃游戏");
        primaryStage.setScene(scene);
        primaryStage.show();
        timer.start();
    }
    private void createGameOverMenu() {
        gameOverMenu = new VBox(20);
        gameOverMenu.setAlignment(Pos.CENTER);
        gameOverMenu.setBackground(new Background(new BackgroundFill(
                Color.rgb(0, 0, 0, 0.7), CornerRadii.EMPTY, Insets.EMPTY)));
        gameOverMenu.setPrefSize(WIDTH, HEIGHT);

        Label gameOverLabel = new Label("游戏结束");
        gameOverLabel.setFont(Font.font(48));
        gameOverLabel.setTextFill(Color.RED);

        Label scoreLabel = new Label("最终分数: 0");
        scoreLabel.setFont(Font.font(36));
        scoreLabel.setTextFill(Color.WHITE);
        scoreLabel.setId("finalScoreLabel");

        Button restartBtn = createMenuButton("重新开始", () -> {
            startNewGame();
            gameOverMenu.setVisible(false);
        });

        Button menuBtn = createMenuButton("返回主菜单", () -> {
            returnToWelcomeMenu();
            gameOverMenu.setVisible(false);
        });

        gameOverMenu.getChildren().addAll(gameOverLabel, scoreLabel, restartBtn, menuBtn);
        gameOverMenu.setVisible(false);
    }
    private void handleKeyPressed(KeyEvent e) {
        if (gameState == GameState.PLAYING) {
            if (e.getCode() == KeyCode.SPACE && !isJumping && !isCharging) {
                isCharging = true;
            } else if (e.getCode() == KeyCode.SPACE && isJumping && isDoubleJumpAvailable && !hasDoubleJumped) {
                // 二段跳
                playerVelocityY = -10;
                hasDoubleJumped = true;
                createParticles(playerX + PLAYER_WIDTH/2, playerY);
                if (soundEnabled) playSound("jump");
            } else if (e.getCode() == KeyCode.ESCAPE) {
                pauseGame();
            }
        } else if (gameState == GameState.PAUSED && e.getCode() == KeyCode.ESCAPE) {
            resumeGame();
        }
        // 移除了R键重新开始逻辑
    }
    private void handleKeyReleased(KeyEvent e) {
        if (gameState == GameState.PLAYING && e.getCode() == KeyCode.SPACE) {
            if (isCharging && !isJumping) {
                // 执行跳跃
                isJumping = true;
                isCharging = false;
                playerVelocityY = -jumpCharge;
                jumpCharge = 0;
                createParticles(playerX + PLAYER_WIDTH/2, playerY + PLAYER_HEIGHT);
                if (soundEnabled) playSound("jump");
            } else if (isJumping && isDoubleJumpAvailable && !hasDoubleJumped) {
                // 二段跳
                playerVelocityY = -10;
                hasDoubleJumped = true;
                createParticles(playerX + PLAYER_WIDTH/2, playerY);
                if (soundEnabled) playSound("jump");
            }
        }
    }
    private void createWelcomeMenu() {
        welcomeMenu = new VBox(20);
        welcomeMenu.setAlignment(Pos.CENTER);
        welcomeMenu.setBackground(new Background(new BackgroundFill(
                Color.rgb(0, 0, 0, 0.7), CornerRadii.EMPTY, Insets.EMPTY)));
        welcomeMenu.setPrefSize(WIDTH, HEIGHT);

        Label titleLabel = new Label("跳跃冒险");
        titleLabel.setFont(Font.font(48));
        titleLabel.setTextFill(Color.WHITE);

        Button newGameBtn = createMenuButton("新游戏", this::startNewGame);
        Button continueBtn = createMenuButton("继续游戏", this::continueGame);
        continueBtn.setDisable(true); // 初始时禁用
        Button instructionsBtn = createMenuButton("游戏说明", this::showInstructions);
        Button soundBtn = createMenuButton("音效: 开", this::toggleSound);
        Button exitBtn = createMenuButton("退出游戏", () -> System.exit(0));

        welcomeMenu.getChildren().addAll(titleLabel, newGameBtn, continueBtn,
                instructionsBtn, soundBtn, exitBtn);
    }
    private void createPauseMenu() {
        pauseMenu = new VBox(20);
        pauseMenu.setAlignment(Pos.CENTER);
        pauseMenu.setBackground(new Background(new BackgroundFill(
                Color.rgb(0, 0, 0, 0.7), CornerRadii.EMPTY, Insets.EMPTY)));
        pauseMenu.setPrefSize(WIDTH, HEIGHT);

        Label pauseLabel = new Label("游戏暂停");
        pauseLabel.setFont(Font.font(48));
        pauseLabel.setTextFill(Color.WHITE);

        Button resumeBtn = createMenuButton("继续游戏", this::resumeGame);
        Button newGameBtn = createMenuButton("新游戏", this::startNewGame);
        Button exitToMenuBtn = createMenuButton("返回主菜单", this::returnToWelcomeMenu);

        pauseMenu.getChildren().addAll(pauseLabel, resumeBtn, newGameBtn, exitToMenuBtn);
    }
    private void createInstructionsLabel() {
        instructionsLabel = new Label(
                "游戏说明:\n\n" +
                        "1. 按住空格键蓄力，释放跳跃\n" +
                        "2. 落地后有几率获得二段跳能力\n" +
                        "3. 在空中可以再次按空格键二段跳\n" +
                        "4. 避开障碍物获得分数\n" +
                        "5. 分数越高，游戏速度越快\n" +
                        "6. 每10分增加分数倍率\n\n" +
                        "按ESC返回");
        instructionsLabel.setFont(Font.font(20));
        instructionsLabel.setTextFill(Color.WHITE);
        instructionsLabel.setTextAlignment(TextAlignment.CENTER);
        instructionsLabel.setBackground(new Background(new BackgroundFill(
                Color.rgb(0, 0, 0, 0.8), CornerRadii.EMPTY, Insets.EMPTY)));
        instructionsLabel.setPrefSize(WIDTH, HEIGHT);
        instructionsLabel.setAlignment(Pos.CENTER);
        instructionsLabel.setVisible(false);

        instructionsLabel.setOnMouseClicked(e -> hideInstructions());
    }
    private Button createMenuButton(String text, Runnable action) {
        Button button = new Button(text);
        button.setFont(Font.font(20));
        button.setStyle("-fx-background-color: #4a4a4a; -fx-text-fill: white;");
        button.setPrefWidth(200);
        button.setPrefHeight(40);
        button.setOnAction(e -> {
            action.run();
            if (soundEnabled) playSound("click");
        });
        return button;
    }
    private void spawnObstacle() {
        int type = random.nextInt(3);
        double height = OBSTACLE_HEIGHT;
        double y = GROUND_HEIGHT - height;

        if (random.nextDouble() < 0.3) {
            height *= 1.5;
            y = GROUND_HEIGHT - height;
        }

        if (random.nextDouble() < 0.1) {
            height *= 0.7;
            y = GROUND_HEIGHT - height;
        }

        obstacles.add(new Obstacle(WIDTH, y, OBSTACLE_WIDTH, height, type));
    }
    private void update() {
        if (gameState != GameState.PLAYING) return;

        updateParticles();

        // 根据分数调整速度
        if (score > 0 && score % speedIncreaseInterval == 0) {
            currentSpeed = baseSpeed + (score / speedIncreaseInterval) * speedIncreaseFactor;
        }

        // 跳跃蓄力
        if (isCharging) {
            jumpCharge = Math.min(jumpCharge + 0.2, MAX_JUMP_CHARGE);
        }

        // 跳跃逻辑
        if (isJumping) {
            playerY += playerVelocityY;
            playerVelocityY += GRAVITY;

            // 落地检测
            if (playerY >= GROUND_HEIGHT - PLAYER_HEIGHT) {
                playerY = GROUND_HEIGHT - PLAYER_HEIGHT;
                isJumping = false;
                hasDoubleJumped = false;
                isDoubleJumpAvailable = random.nextDouble() < 0.3;
                createParticles(playerX + PLAYER_WIDTH/2, playerY + PLAYER_HEIGHT);
                if (soundEnabled) playSound("land");
            }
        }

        // 障碍物移动
        for (int i = 0; i < obstacles.size(); i++) {
            Obstacle obstacle = obstacles.get(i);
            obstacle.x -= currentSpeed;

            if (obstacle.x < -obstacle.width) {
                obstacles.remove(i);
                i--;
                score += scoreMultiplier;
                if (score > highScore) highScore = score;
                spawnObstacle();

                if (score > 0 && score % 10 == 0) {
                    scoreMultiplier++;
                    if (soundEnabled) playSound("multiplier");
                }
            }
        }

        // 碰撞检测
        for (Obstacle obstacle : obstacles) {
            if (isColliding(obstacle)) {
                gameOver();
                break;
            }
        }
    }
    private void updateParticles() {
        for (int i = 0; i < particles.size(); i++) {
            Particle p = particles.get(i);
            p.update();
            if (p.isDead()) {
                particles.remove(i);
                i--;
            }
        }
    }
    private void createParticles(double x, double y) {
        for (int i = 0; i < 15; i++) {
            particles.add(new Particle(x, y));
        }
    }
    private boolean isColliding(Obstacle obstacle) {
        Bounds playerBounds = new javafx.geometry.BoundingBox(
                playerX, playerY, PLAYER_WIDTH, PLAYER_HEIGHT);
        Bounds obstacleBounds = new javafx.geometry.BoundingBox(
                obstacle.x, obstacle.y, obstacle.width, obstacle.height);

        return playerBounds.intersects(obstacleBounds);
    }
    private void render() {
        gc.setFill(Color.LIGHTBLUE);
        gc.fillRect(0, 0, WIDTH, HEIGHT);

        gc.setFill(Color.DARKGREEN);
        gc.fillRect(0, GROUND_HEIGHT, WIDTH, HEIGHT - GROUND_HEIGHT);

        gc.setFill(Color.RED);
        gc.fillRect(playerX, playerY, PLAYER_WIDTH, PLAYER_HEIGHT);

        if (isCharging) {
            gc.setFill(Color.YELLOW);
            gc.fillRect(playerX, playerY - 10, jumpCharge * 2, 5);
        }

        if (isDoubleJumpAvailable && isJumping && !hasDoubleJumped) {
            gc.setFill(Color.CYAN);
            gc.fillOval(playerX - 5, playerY - 15, 10, 10);
        }

        for (Obstacle obstacle : obstacles) {
            switch (obstacle.type) {
                case 0: gc.setFill(Color.BLACK); break;
                case 1: gc.setFill(Color.DARKRED); break;
                case 2: gc.setFill(Color.DARKBLUE); break;
            }
            gc.fillRect(obstacle.x, obstacle.y, obstacle.width, obstacle.height);
        }

        for (Particle p : particles) {
            gc.setFill(p.color);
            gc.fillOval(p.x, p.y, p.size, p.size);
        }

        gc.setFill(Color.WHITE);
        gc.setFont(Font.font(20));
        gc.fillText("分数: " + score, 20, 30);
        gc.fillText("最高分: " + highScore, 20, 60);
        gc.fillText("倍率: x" + scoreMultiplier, 20, 90);
        gc.fillText(String.format("速度: %.1f", currentSpeed), 20, 120);
    }
    private void startNewGame() {
        // 重置游戏状态
        playerX = 100;
        playerY = GROUND_HEIGHT - PLAYER_HEIGHT;
        playerVelocityY = 0;
        isJumping = false;
        isCharging = false;
        jumpCharge = 0;
        obstacles.clear();
        spawnObstacle();
        currentSpeed = baseSpeed;
        score = 0;
        scoreMultiplier = 1;
        particles.clear();
        isDoubleJumpAvailable = false;
        hasDoubleJumped = false;

        gameState = GameState.PLAYING;

        // 隐藏所有菜单
        welcomeMenu.setVisible(false);
        pauseMenu.setVisible(false);
        gameOverMenu.setVisible(false);
        instructionsLabel.setVisible(false);

        if (soundEnabled) playSound("start");
    }
    private void continueGame() {
        // 目前没有保存游戏功能，所以与开始新游戏相同
        startNewGame();
    }
    private void pauseGame() {
        gameState = GameState.PAUSED;
        root.getChildren().add(pauseMenu);
        if (soundEnabled) playSound("pause");
    }
    private void resumeGame() {
        gameState = GameState.PLAYING;
        root.getChildren().remove(pauseMenu);
        if (soundEnabled) playSound("resume");
    }
    private void returnToWelcomeMenu() {
        gameState = GameState.WELCOME;

        // 隐藏所有其他界面
        pauseMenu.setVisible(false);
        gameOverMenu.setVisible(false);
        instructionsLabel.setVisible(false);

        // 显示欢迎菜单
        welcomeMenu.setVisible(true);

        // 更新继续按钮状态
        if (welcomeMenu.getChildren().size() > 2) {
            ((Button)welcomeMenu.getChildren().get(2)).setDisable(score == 0);
        }

        if (soundEnabled) playSound("menu");
    }
    private void gameOver() {
        gameState = GameState.GAME_OVER;

        // 更新分数显示
        Label scoreLabel = (Label) gameOverMenu.lookup("#finalScoreLabel");
        if (scoreLabel != null) {
            scoreLabel.setText("最终分数: " + score);
        }

        // 隐藏其他所有界面
        welcomeMenu.setVisible(false);
        pauseMenu.setVisible(false);
        instructionsLabel.setVisible(false);

        // 显示游戏结束菜单
        gameOverMenu.setVisible(true);

        if (soundEnabled) playSound("gameover");
    }
    private void showInstructions() {
        instructionsLabel.setVisible(true);
        root.getChildren().add(instructionsLabel);
    }
    private void hideInstructions() {
        instructionsLabel.setVisible(false);
        root.getChildren().remove(instructionsLabel);
    }
    private void toggleSound() {
        soundEnabled = !soundEnabled;
        Button soundBtn = (Button)welcomeMenu.getChildren().get(4);
        soundBtn.setText("音效: " + (soundEnabled ? "开" : "关"));
    }
    private void resetGame() {
        playerX = 100;
        playerY = GROUND_HEIGHT - PLAYER_HEIGHT;
        playerVelocityY = 0;
        isJumping = false;
        isCharging = false;
        jumpCharge = 0;
        obstacles.clear();
        spawnObstacle();
        currentSpeed = baseSpeed;
        score = 0;
        scoreMultiplier = 1;
        particles.clear();
        isDoubleJumpAvailable = false;
        hasDoubleJumped = false;

        // 确保游戏结束菜单隐藏
        gameOverMenu.setVisible(false);
    }
    private void playSound(String sound) {
        System.out.println("播放音效: " + sound); // 实际项目中应使用AudioClip
    }
    public static void main(String[] args) {
        launch(args);
    }
    class Obstacle {
        double x, y;
        double width, height;
        int type;
        Obstacle(double x, double y, double width, double height, int type) {
            this.x = x;
            this.y = y;
            this.width = width;
            this.height = height;
            this.type = type;
        }
    }
    class Particle {
        double x, y;
        double vx, vy;
        double size;
        Color color;
        int life;
        Particle(double x, double y) {
            this.x = x;
            this.y = y;
            this.vx = Math.random() * 4 - 2;
            this.vy = Math.random() * -3 - 1;
            this.size = Math.random() * 3 + 1;
            this.color = Color.rgb(255, (int)(Math.random() * 155 + 100), 0, Math.random() * 0.5 + 0.5);
            this.life = (int)(Math.random() * 30 + 20);
        }
        void update() {
            x += vx;
            y += vy;
            vy += 0.1;
            life--;
        }
        boolean isDead() {
            return life <= 0;
        }
    }
}